import fileService from "../service/file.service.js";
import userService from "../service/user.service.js";
import downloadService from "../service/download.service.js";
import fse from "fs-extra";
import path from "path";
import "express-async-errors";
import jwt from "jsonwebtoken";
import archiver from "archiver"
import { nanoid } from "nanoid";

import ffmpeg from "fluent-ffmpeg";
ffmpeg.setFfmpegPath(process.env.FFMPEG_PATH);
const HLS_DIR = path.resolve("uploads", "hls");
const UPLOADS_DIR = path.resolve("uploads", "files");
const ARCHIVE_DIR = path.resolve("uploads", "archives");
const DOWNLOAD_SIZE_LIMIT = 1024 * 1024 * 100; // 100mb

//创建目录
const createDirectory = async (req, res) => {
  const userId = req.user.id;
  const parentId = req.params.parentId ?? null;
  let filename = req.body.filename;

  const checkFilename = await fileService.checkFilename(userId, parentId, filename);
  if (checkFilename) {
    const suffix = nanoid(4);
    const ext = path.extname(filename);
    filename = `${path.basename(filename, ext)}-${suffix}${ext}`;
  }

  await fileService.createFile({
    userId,
    parentId,
    filename,
    type: "folder",
    sort: 0,
    isFolder: true,
  });
  res.status(201).json({ message: "创建成功" });
};

//删除一个
const recycleFile = async (req, res) => {
  const fileId = req.params.id;
  const userId = req.user.id;

  const file = await fileService.getFileById(fileId);
  if (!file || file.isDeleted || file.userId !== userId) {
    return res.status(404).json({ message: "文件不存在" });
  }

  const { Ids, totalSize } = await fileService.getDescendantInfos([fileId], userId, false);
  const user = await userService.getUserById(userId);
  await fileService.updateFiles(Ids, { isDeleted: true, deletedAt: new Date() });
  if (Number(user.used) - totalSize < 0) {
    console.error("删除的文件超出用户使用的空间");
    await userService.updateUser(userId, { used: 0 });
  } else {
    await userService.decreaseUsed(req.user.id, totalSize);
  }

  res.json({ message: "删除成功" });
};

//批量删除
const recycleFiles = async (req, res) => {
  const fileIds = req.body.fileIds;
  const userId = req.user.id;

  if (!Array.isArray(fileIds) || fileIds.length === 0) {
    return res.status(400).json({ message: "请提供有效的文件ID" });
  }

  const { Ids, totalSize } = await fileService.getDescendantInfos(fileIds, userId, false);
  const user = await userService.getUserById(userId);
  await fileService.updateFiles(Ids, { isDeleted: true, deletedAt: new Date() });

  if (Number(user.used) - totalSize < 0) {
    console.error("删除的文件超出用户使用的空间");
    await userService.updateUser(userId, { used: 0 });
  } else {
    await userService.decreaseUsed(req.user.id, totalSize);
  }

  res.json({ message: `成功删除 ${Ids.length} 个项目` });
};

//清除一个
const deleteFile = async (req, res) => {
  const fileId = req.params.id;
  const file = await fileService.getFileById(fileId);
  const userId = req.user.id;
  if (!file || file.userId !== userId) {
    return res.status(404).json({ message: "文件不存在" });
  }
  if (!file.isFolder) {
    const filePath = path.resolve(UPLOADS_DIR, file.hash);
    fse.removeSync(filePath);
  }
  await fileService.deleteFile(fileId);
  res.json({ message: "文件清除成功" });
};

//批量硬删除
const deleteFiles = async (req, res) => {
  const fileIds = req.body.fileIds;
  const userId = req.user.id;

  if (!Array.isArray(fileIds) || fileIds.length === 0) {
    return res.status(400).json({ message: "请提供有效的文件ID列表" });
  }

  const { count } = await fileService.deleteFiles(fileIds, userId);
  res.json({ message: `成功清除 ${count} 个项目` });
};

//恢复文件
const restoreFile = async (req, res) => {
  const fileId = req.params.id;
  const userId = req.user.id;

  const file = await fileService.getFileById(fileId);
  if (!file || !file.isDeleted || file.userId !== userId) {
    return res.status(404).json({ message: "文件不存在或者没有被删除" });
  }

  const { Ids, totalSize } = await fileService.getDescendantInfos([fileId], userId, true);
  const user = await userService.getUserById(userId);
  if (Number(user.used) + totalSize > Number(user.storage)) {
    console.error("恢复的文件超出用户使用的空间");
    return res.status(400).json({ message: "恢复的文件超出用户使用的空间" });
  }
  await fileService.updateFiles(Ids, { isDeleted: false });
  await userService.increaseUsed(userId, totalSize);

  res.json({ message: "文件恢复成功" });
};

//批量恢复
const restoreFiles = async (req, res) => {
  const fileIds = req.body.fileIds;
  const userId = req.user.id;

  if (!Array.isArray(fileIds) || fileIds.length === 0) {
    return res.status(400).json({ message: "请提供有效的文件ID列表" });
  }

  const { Ids, totalSize } = await fileService.getDescendantInfos(fileIds, userId, true);
  const user = await userService.getUserById(userId);
  if (Number(user.used) + totalSize > Number(user.storage)) {
    console.error("恢复的文件超出用户使用的空间");
    return res.status(400).json({ message: "恢复的文件超出用户使用的空间" });
  }
  await fileService.updateFiles(Ids, { isDeleted: false });
  await userService.increaseUsed(userId, totalSize);

  res.json({ message: `成功恢复 ${Ids.count} 个项目` });
};

//重命名
const rename = async (req, res) => {
  const fileId = req.params.id;
  const userId = req.user.id;
  let filename = req.body.filename;

  const file = await fileService.getFileById(fileId);
  if (!file || file.isDeleted || file.userId !== userId) {
    return res.status(404).json({ message: "文件不存在" });
  }
  const parentId = file.parentId ?? null;
  const checkFilename = await fileService.checkFilename(userId, parentId, filename);
  if (checkFilename) {
    const suffix = nanoid(4);
    const ext = path.extname(filename);
    filename = `${path.basename(filename, ext)}-${suffix}${ext}`;
  }

  await fileService.updateFile(fileId, { filename });
  res.json({ message: "重命名成功" });
};

//直接下载文件 一般不使用
const downloadFile = async (req, res) => {
  const fileId = req.params.id;
  const file = await fileService.getFileById(fileId);
  if (!file || file.isDeleted || file.userId !== req.user.id) {
    return res.status(404).json({ message: "文件不存在" });
  }
  await downloadService.createDownload(fileId, req.ip, req.get('User-Agent'));

  const ext = path.extname(file.filename).toLowerCase();
  const fileName = file.hash + ext;
  const filePath = path.resolve(UPLOADS_DIR, fileName);

  console.log("直接下载文件", filePath);
  res.setHeader(
    "Content-Disposition",
    `attachment; filename="${encodeURIComponent(file.filename)}"`,
  );
  res.setHeader("Content-Type", file.type);

  const fileStream = fse.createReadStream(filePath);
  fileStream.pipe(res);

  fileStream.on("error", (err) => {
    console.error("下载错误:", err);
    res.status(500).json({ message: "文件下载失败" });
  });
};

// 生成带签名的下载直链 由浏览器直接下载 避免权限验证
const getFileUrl = async (req, res) => {
  const { fileId } = req.params;
  const userId = req.user.id;

  const file = await fileService.getFileById(fileId);
  if (!file || file.userId !== userId) {
    return res.status(404).json({ message: "文件不存在" });
  }

  await downloadService.createDownload(fileId, req.ip, req.get('User-Agent'))
  const signature = jwt.sign(
    { hash: file.hash, filename: file.filename, type: file.type },
    process.env.JWT_SECRET,
    { expiresIn: process.env.URL_EXPIRES_IN },
  );
  res.json({ signature });
};

//使用签名下载文件
const downloadFileByUrl = async (req, res) => {
  const { signature } = req.params;
  let decoded;
  try {
    decoded = jwt.verify(signature, process.env.JWT_SECRET);
  } catch (error) {
    return res.status(400).json({ message: "无效的签名" });
  }

  let filePath;
  console.log('请求使用签名下载文件', decoded)
  if (decoded.isArchive) {
    const fileName = decoded.hash + ".zip"
    filePath = path.resolve(ARCHIVE_DIR, fileName);
  } else {
    const ext = path.extname(decoded.filename).toLowerCase();
    const fileName = decoded.hash + ext;
    filePath = path.resolve(UPLOADS_DIR, fileName);
  }

  if (!fse.existsSync(filePath)) {
    return res.status(404).json({ message: "文件不存在" });
  }

  res.setHeader(
    "Content-Disposition",
    `attachment; filename="${encodeURIComponent(decoded.filename)}"`,
  );
  res.setHeader("Content-Type", decoded.type);

  const fileStream = fse.createReadStream(filePath);
  fileStream.pipe(res);

  fileStream.on("error", (err) => {
    console.error("下载错误:", err);
    res.status(500).json({ message: "文件下载失败" });
  });
};

// uploads/hls/play/hash/${signature} 没用了这个接口
const palyVideoByUrl = async (req, res) => {
  const { signature } = req.params;
  let decoded;

  try {
    decoded = jwt.verify(signature, process.env.JWT_SECRET);
  } catch (error) {
    return res.status(400).json({ message: "无效的签名" });
  }
  const videoTypes = [".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm"];
  const ext = path.extname(decoded.filename).toLowerCase();
  if (!videoTypes.includes(ext)) {
    return res.status(400).json({ message: "不支持的视频格式" });
  }
  const fileName = decoded.hash + ext;
  const filePath = path.resolve(UPLOADS_DIR, fileName);

  if (!fse.existsSync(filePath)) {
    return res.status(404).json({ message: "文件不存在" });
  }

  // HLS 输出目录
  const hlsDir = path.resolve(HLS_DIR, decoded.hash);
  const hlsPath = path.join(hlsDir, "index.m3u8");

  console.log("请求播放资源", filePath);

  if (!fse.existsSync(hlsPath)) {
    return res.status(404).json({ message: "视频资源不存在" });
  }

  const m3u8Content = await fse.readFile(hlsPath, 'utf8');
  if (!m3u8Content.includes('#EXT-X-ENDLIST')) {
    return res.status(404).json({ message: "文件正在转码" });
  }

  res.setHeader('Content-Type', 'application/vnd.apple.mpegurl');
  return res.send(m3u8Content);
};

//获取文件
const getPage = async (req, res) => {
  const {
    pageSize = 10,
    current = 1,
    searchText = "",
    sortField,
    sortOrder,
  } = req.query;
  const parentId = req.params.parentId ?? null;

  const userId = req.user.id;

  const data = await fileService.getFilePage(
    userId,
    parentId,
    pageSize,
    current,
    searchText,
    sortField,
    sortOrder,
  );
  res.json({ data, message: "获取成功" });
};

//获取文件
const getFileInfo = async (req, res) => {
  const fileId = req.params.id;
  const userId = req.user.id;
  const file = await fileService.getFileInfoById(fileId);
  if (!file || file.isDeleted || file.userId !== userId) {
    return res.status(404).json({ message: "文件不存在" });
  }
  res.json({ data: file, message: "获取成功" });
};

//获取文件夹
const getFolders = async (req, res) => {
  const parentId = req.params.parentId ?? null;
  const userId = req.user.id;

  const data = await fileService.getChildren(userId, parentId);

  res.json({ data, message: "获取成功" });
};

// 移动文件
const moveFile = async (req, res) => {
  const fileId = req.params.id;
  const parentId = req.body.parentId ?? null;
  const userId = req.user.id;

  const file = await fileService.getFileById(fileId);
  if (!file || file.isDeleted || file.userId !== userId) {
    return res.status(404).json({ message: "文件不存在" });
  }

  if (parentId !== null) {
    const file = await fileService.getFileById(parentId);
    if (!file || file.isDeleted || !file.isFolder || file.userId !== userId) {
      return res.status(404).json({ message: "目标文件夹不存在" });
    }
    if (await isCircularReference(fileId, parentId)) {
      return res.status(400).json({ message: "不能移动到此位置" });
    }
  }

  const checkFilename = await fileService.checkFilename(userId, parentId, file.filename);
  if (checkFilename) {
    const suffix = nanoid(4);
    const ext = path.extname(file.filename);
    file.filename = `${path.basename(file.filename, ext)}-${suffix}${ext}`;

    await fileService.updateFile(fileId, { filename: file.filename });
  }

  await fileService.updateFile(fileId, { parentId });
  res.json({ message: "移动成功" });
};

// 尝试将文件夹移动到自己的子文件夹中, 会导致循环引用
async function isCircularReference(sourceId, targetParentId) {
  let currentParentId = targetParentId;

  while (currentParentId) {
    if (currentParentId === sourceId) {
      return true; // 发现循环
    }
    // 向上查找父级
    const parent = await fileService.getFileById(currentParentId);
    currentParentId = parent?.parentId;
  }

  return false;
}

//复制到文件夹下
const copyFile = async (req, res) => {
  const fileId = req.params.id;
  const parentId = req.body.parentId ?? null;
  const userId = req.user.id;

  const file = await fileService.getFileById(fileId);
  if (!file || file.isDeleted || file.userId !== userId) {
    return res.status(404).json({ message: "文件不存在" });
  }

  const { message, success } = await copyDo(fileId, parentId, userId, file.userId, file.filename);
  if (!success) {
    return res.status(400).json({ message });
  }
  res.json({ message: "复制成功" });
}

async function copyDo(fileId, parentId, userId, fileUserId, filename) {
  if (parentId !== null) {
    const file = await fileService.getFileById(parentId);
    if (!file || file.isDeleted || !file.isFolder || file.userId !== userId) {
      // return res.status(404).json({ message: "目标文件夹不存在" });
      return { message: "目标文件夹不存在", success: false };
    }
  }

  const isCircular = await isCircularReference(fileId, parentId);
  if (isCircular) {
    // return res.status(400).json({ message: "不能复制到此位置" });
    return { message: "不能复制到此位置", success: false };
  }

  const { Ids, totalSize } = await fileService.getDescendantInfos([fileId], fileUserId, false);
  const user = await userService.getUserById(userId);
  if (Number(user.used) + totalSize > Number(user.storage)) {
    console.error("复制的文件超出用户使用的空间");
    // return res.status(400).json({ message: "复制的文件超出用户使用的空间" });
    return { message: "复制的文件超出用户使用的空间", success: false };
  }

  const files = await fileService.getIdsFile(Ids);
  const fileTree = buildFileTree(files, fileId);

  const renamedFileTree = await renameTopLevelFiles(fileTree, userId, parentId, filename);
  await copyFileSync(renamedFileTree, parentId, userId);
  await userService.increaseUsed(userId, totalSize);
  // res.json({ message: "复制成功" });
  return { message: "复制成功", success: true };
}

async function renameTopLevelFiles(fileTree, userId, parentId, filename) {
  await Promise.all(
    fileTree.map(async (root) => {
      const checkFilename = await fileService.checkFilename(userId, parentId, filename);
      if (checkFilename) {
        const suffix = nanoid(4);
        const ext = path.extname(root.filename);
        root.filename = `${path.basename(root.filename, ext)}-${suffix}${ext}`;
      }
    })
  );
  return fileTree;
}


const copyFileSync = async (fileTree, parentId = null, userId) => {
  for (const file of fileTree) {
    const newFile = await fileService.createFile({
      userId,
      parentId,
      size: file.size,
      filename: file.filename,
      hash: file.hash,
      type: file.type,
      sort: file.sort,
      isFolder: file.isFolder,
    })
    if (file.isFolder && file.children) {
      await copyFileSync(file.children, newFile.id, userId);
    }
  }
}

// 批量移动文件
const moveFiles = async (req, res) => {
  const fileIds = req.body.fileIds;
  const parentId = req.body.parentId ?? null;

  if (!Array.isArray(fileIds) || fileIds.length === 0) {
    return res.status(400).json({ message: "请提供有效的文件ID" });
  }

  // 检查目标文件夹（如果提供了parentId）
  if (parentId !== null) {
    const file = await fileService.getFileById(parentId);
    if (!file || file.isDeleted || !file.isFolder) {
      return res.status(404).json({ message: "目标文件夹不存在" });
    }
  }

  // 检查所有文件是否存在并检查循环引用
  const files = await Promise.all(
    fileIds.map((id) => fileService.getFileById(id)),
  );

  // 检查是否有不存在的文件
  if (files.some((file) => !file || file.isDeleted)) {
    return res.status(404).json({ message: "部分文件不存在" });
  }

  // 检查循环引用（只针对文件夹）
  for (const file of files) {
    if (file.isFolder && parentId !== null) {
      if (await isCircularReference(file.id, parentId)) {
        return res.status(400).json({ message: `不能移动到此位置` });
      }
    }
  }

  // 重命名文件（如果文件名重复）
  for (const file of files) {
    const checkFilename = await fileService.checkFilename(file.userId, parentId, file.filename);
    if (checkFilename) {
      const suffix = nanoid(4);
      const ext = path.extname(file.filename);
      const filename = `${path.basename(file.filename, ext)}-${suffix}${ext}`;
      await fileService.updateFile(file.id, { filename });
    }
  }

  // 执行批量移动
  await fileService.updateFiles(fileIds, { parentId });

  res.json({ message: `${fileIds.length} 个文件移动成功` });
};

const getRecycle = async (req, res) => {
  const userId = req.user.id;
  const data = await fileService.getRecycle(userId);
  res.json({ data, message: "获取成功" });
};

const downloadFolder = async (req, res) => {
  const folderId = req.params.folderId;
  const userId = req.user.id;
  const folder = await fileService.getFileById(folderId);

  if (!folder || !folder.isFolder || folder.userId !== userId) {
    return res.status(404).json({ message: "文件夹不存在" });
  }

  const { signature, success, message } = await archiveFolder(folderId, userId);
  if (!success) {
    return res.status(400).json({ message });
  }
  await downloadService.createDownload(folderId, req.ip, req.get('User-Agent'));
  res.json({ signature });
}

const archiveFolder = async (folderId, userId) => {
  const { Ids, totalSize } = await fileService.getDescendantInfos([folderId], userId, false);
  if (totalSize > DOWNLOAD_SIZE_LIMIT) {
    console.error("下载的文件夹过大");
    return { success: false, message: "文件夹过大，不能下载" }
  }
  if (totalSize === 0) {
    console.error("下载的文件夹为空");
    return { success: false, message: "文件夹为空，不能下载" }
  }

  const files = await fileService.getIdsFile(Ids);
  const fileTree = buildFileTree(files, folderId);
  const filename = fileTree[0].filename + '.zip' || 'archive.zip';

  const hash = nanoid(8);
  const writeStream = fse.createWriteStream(path.join(ARCHIVE_DIR, `${hash}.zip`));
  const archive = archiver('zip', { zlib: { level: 0 } }); // 0：仅打包不压缩
  archive.pipe(writeStream);

  await addFilesToArchive(archive, fileTree);
  await archive.finalize();
  const signature = jwt.sign(
    { hash, filename, type: 'application/zip', isArchive: true },
    process.env.JWT_SECRET,
    { expiresIn: process.env.URL_EXPIRES_IN },
  );
  return { signature, success: true }
}

const downloadFiles = async (req, res) => {
  const fileIds = req.body.Ids;
  const userId = req.user.id;

  const { Ids, totalSize } = await fileService.getDescendantInfos(fileIds, userId, false);

  console.log("下载总大小", totalSize)
  console.log("下载文件数", Ids)
  if (totalSize > DOWNLOAD_SIZE_LIMIT) {
    console.error("下载的文件夹过大");
    return res.status(400).json({ message: "文件过大，不能下载" });
  }
  if (totalSize === 0) {
    console.error("下载的文件夹为空");
    return res.status(400).json({ message: "文件为空，不能下载" });
  }
  const files = await fileService.getIdsFile(Ids);
  const fileTree = buildFileTree(files, Ids);
  console.log("文件树", fileTree)

  const filename = fileTree.map((file) => file.filename).join('+') + '.zip' || 'archive.zip';
  console.log("文件名", filename)

  const hash = nanoid(8);
  const writeStream = fse.createWriteStream(path.join(ARCHIVE_DIR, `${hash}.zip`));
  const archive = archiver('zip', { zlib: { level: 0 } }); // 0：仅打包不压缩
  archive.pipe(writeStream);

  await addFilesToArchive(archive, fileTree);
  await archive.finalize();

  const signature = jwt.sign(
    { hash, filename, type: 'application/zip', isArchive: true },
    process.env.JWT_SECRET,
    { expiresIn: process.env.URL_EXPIRES_IN },
  );
  await downloadService.createDownload(folderId, req.ip, req.get('User-Agent'));

  res.json({ signature });
}

function buildFileTree(items, rootIds = [null]) {
  if (!Array.isArray(rootIds)) {
    rootIds = [rootIds];
  }

  // 1. 找出所有匹配 rootIds 的项（作为顶级节点）
  const rootNodes = items.filter(item => rootIds.includes(item.id));

  // 2. 为每个顶级节点构建子树
  return rootNodes.map(root => ({
    ...root,
    children: root.isFolder ? buildFileTree(items, items
      .filter(item => item.parentId === root.id)
      .map(item => item.id)
    ) : [],
  }));
}


async function addFilesToArchive(archive, fileTree, basePath = '') {
  for (const item of fileTree) {
    const currentPath = path.join(basePath, item.filename);

    if (item.isFolder) {
      if (item.children && item.children.length > 0) {
        // 添加空目录
        archive.append(null, { name: currentPath + '/' });
        await addFilesToArchive(archive, item.children, currentPath);
      }
    } else {
      // 添加实际文件
      const ext = path.extname(item.filename).toLowerCase();
      const fileName = item.hash + ext;
      const filePath = path.resolve(UPLOADS_DIR, fileName);
      if (fse.existsSync(filePath)) {
        archive.file(filePath, { name: currentPath });
      }
    }
  }
}

export default {
  createDirectory,
  recycleFile,
  recycleFiles,
  deleteFile,
  deleteFiles,
  restoreFile,
  restoreFiles,
  rename,
  downloadFile,
  getFileUrl,
  downloadFileByUrl,
  getPage,
  getFileInfo,
  getFolders,
  moveFile,
  moveFiles,
  getRecycle,
  downloadFolder,
  downloadFiles,
  copyFile,
  archiveFolder,
  copyDo,
  palyVideoByUrl
};
